package org.ipmsg.core;

import org.ipmsg.core.event.DatagramEvent;
import org.ipmsg.core.event.DatagramReceiveListener;
import org.ipmsg.core.event.DatagramSendListener;
import org.ipmsg.core.event.MessageEvent;
import org.ipmsg.core.event.MessageListener;
import org.ipmsg.core.file.PublishFiles;
import org.ipmsg.core.net.BroadcastSend;
import org.ipmsg.core.net.DatagramReceive;
import org.ipmsg.core.net.DatagramSend;
import java.net.*;
import java.util.ArrayList;
import java.util.Hashtable;

public class Messenger
        implements DatagramSendListener, DatagramReceiveListener {

    private MessageListener mainListener;
    private DatagramSocket udpSocket;
    private Hashtable receiveThreads;
    private PublishFiles publishFiles;
    private MemberList members;
    private long serial;
    private InetAddress assignAddress;
    private ArrayList broadcastAddress;
    private int listenerPort;
    private int maxMessageBuffer;
    private String nickName;
    private String host;
    private String sysUser;

    public Messenger() {
        members = new MemberList();
        receiveThreads = new Hashtable();
        listenerPort = Protocol.DEFAULT_PORT;
        maxMessageBuffer = 16384;
        nickName = "";
        host = "";
        try {
            host = InetAddress.getLocalHost().getHostName();
        } catch (Exception exception) {
        }
        sysUser = System.getProperty("user.name", "No Name");
    }

    public void setAssignAddress(InetAddress assignAddress) {
        this.assignAddress = assignAddress;
    }

    public void setListenerPort(int listenerPort) {
        this.listenerPort = listenerPort;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public void setBroadcastAddress(ArrayList broadcastAddress) {
        this.broadcastAddress = broadcastAddress;
    }

    public int getListenerPort() {
        return listenerPort;
    }

    public MemberList getMemberList() {
        return members;
    }

    public void addMainListener(MessageListener listener) {
        mainListener = listener;
    }

    public void start() {
        ServerSocket fileSocket = null;
        try {
            if (assignAddress != null) {
                udpSocket = new DatagramSocket(listenerPort, assignAddress);
                fileSocket = new ServerSocket(listenerPort, 50, assignAddress);
            } else {
                udpSocket = new DatagramSocket(listenerPort);
                fileSocket = new ServerSocket(listenerPort);
            }
        } catch (Exception ex) {
            mainListener.showPortError(listenerPort);
        }
        DatagramReceive receive = new DatagramReceive(udpSocket, maxMessageBuffer);
        receive.addIPMsgRecvListener(this);
        receive.start();
        publishFiles = new PublishFiles(fileSocket, maxMessageBuffer);
        Message loginPacket = makeMessage(Protocol.FILEATTACHOPT | Protocol.FILE_REGULAR, nickName);
        sendBroadcast(loginPacket);
    }

    public void exit() {
        BroadcastSend bs = sendBroadcast(makeMessage(Protocol.BR_EXIT, ""));
        try {
            bs.join();
        } catch (Exception exception) {
        }
    }

    public BroadcastSend sendBroadcast(Message pack) {
        return new BroadcastSend(udpSocket, pack, broadcastAddress, listenerPort);
    }

    public Message makeMessage(long comand, String content) {
        return makeMessage(comand, content, null);
    }

    public Message makeMessage(long comand, String content, Attachment attachment) {
        Message packet = new Message();
        packet.setVersion(Protocol.VERSION);
        packet.setSerial(getSerial());
        packet.setSysUser(sysUser);
        packet.setHost(host);
        packet.setCommand(comand);
        packet.setContent(content);
        packet.setAttachment(attachment);
        return packet;
    }

    public long getSerial() {
        return System.currentTimeMillis() / 1000L + serial++;
    }

    public void sendEvent(DatagramEvent event) {
        receiveThreads.remove(new Long(event.getPack().getSerial()));
        mainListener.sendError(new MessageEvent(this, event.getPack(), members.getMember(event.getAddress().getStringAddress()), event.getAddress()));
    }

    public void receiveEvent(DatagramEvent event) {
        Message tmppack = event.getPack();
        Address tmpaddr = event.getAddress();
        long cmd = tmppack.getCommand() & Protocol.MODE_MASK;
        switch ((int) cmd) {
            default:
                break;

            case 1: // '\001'
                DatagramSend.send(udpSocket, makeMessage(Protocol.FILEATTACHOPT | Protocol.FILE_RETPARENT, nickName), tmpaddr);
            // fall through

            case 3: // '\003'
                memberEntry(tmppack, tmpaddr);
                break;

            case 4: // '\004'
                memberEntry(tmppack, tmpaddr);
                break;

            case 2: // '\002'
                memberExit(tmpaddr);
                break;

            case 32: // ' '
                DatagramSend.send(udpSocket, makeMessage(Protocol.AUTORETOPT | Protocol.RECVMSG, (new Long(tmppack.getSerial())).toString()), tmpaddr);
                mainListener.showReceiveWindow(new MessageEvent(this, tmppack, members.getMember(tmpaddr.getStringAddress()), tmpaddr));
                break;

            case 33: // '!'
                try {
                    Long tmpLong = new Long(tmppack.getContent());
                    DatagramSend ips = (DatagramSend) receiveThreads.get(tmpLong);
                    if (ips != null) {
                        ips.receiveReply();
                        receiveThreads.remove(tmpLong);
                    }
                } catch (NumberFormatException numberformatexception) {
                }
                break;

            case 48: // '0'
                mainListener.showReadMessage(new MessageEvent(this, tmppack, members.getMember(tmpaddr.getStringAddress()), tmpaddr));
                break;

            case 97: // 'a'
                publishFiles.removeAttachment(tmpaddr, tmppack);
                break;
        }
    }

    private String getNickName(Message aPack) {
        String tmpuser = aPack.getContent();
        if (tmpuser == null) {
            return aPack.getSysUser();
        } else {
            return tmpuser;
        }
    }

    public synchronized void memberEntry(Message argpack, Address tmpaddr) {
        Member member = new Member();
        member.setNickName(getNickName(argpack));
        member.setHost(argpack.getHost());
        member.setAddress(tmpaddr.getStringAddress());
        member.setSysUser(argpack.getSysUser());
        member.setGroup("");
        if (!members.isExist(member)) {
            members.addMember(member);
        }
        members.refurbish();
    }

    public synchronized void memberExit(Address tmpaddr) {
        members.removeMember(tmpaddr.getStringAddress());
        members.refurbish();
    }

    public synchronized void refreshUsers() {
        members.removeAll();
        members.refurbish();
        sendBroadcast(makeMessage(Protocol.FILEATTACHOPT | Protocol.FILE_REGULAR, nickName));
    }

    public synchronized void senMsg(Address addrs, Message ipmp) {
        DatagramSend ips = new DatagramSend(udpSocket, ipmp, addrs);
        ips.addIPMsgSendListener(this);
        ips.start();
        receiveThreads.put(new Long(ipmp.getSerial()), ips);
        publishFiles.addAttachment(addrs, ipmp);
    }

    public synchronized void sendMsg(Address addrs[], String msg, long flag, Attachment attachment) {
        if (addrs.length == 1) {
            Message ipmp = makeMessage(Protocol.FILEATTACHOPT | Protocol.FILE_RONLYOPT | Protocol.RECVMSG | flag, msg, attachment);
            DatagramSend ips = new DatagramSend(udpSocket, ipmp, addrs[0]);
            ips.addIPMsgSendListener(this);
            ips.start();
            receiveThreads.put(new Long(ipmp.getSerial()), ips);
            publishFiles.addAttachment(addrs[0], ipmp);
        } else {
            for (int i = 0; i < addrs.length; i++) {
                Message ipmp = makeMessage(Protocol.FILEATTACHOPT | Protocol.MULTICASTOPT | Protocol.SENDMSG | flag, msg, attachment);
                DatagramSend ips = new DatagramSend(udpSocket, ipmp, addrs[i]);
                ips.addIPMsgSendListener(this);
                ips.start();
                receiveThreads.put(new Long(ipmp.getSerial()), ips);
                publishFiles.addAttachment(addrs[i], ipmp);
            }

        }
    }

    public void sendReadMsg(Message argpack, Address tmpaddr) {
        Message ipmp = makeMessage(Protocol.AUTORETOPT | Protocol.READMSG, (new Long(argpack.getSerial())).toString());
        DatagramSend.send(udpSocket, ipmp, tmpaddr);
    }

    public void sendDeleteMsg(Message argpack, Address tmpaddr) {
        Message ipmp = makeMessage(Protocol.AUTORETOPT | Protocol.DELMSG, (new Long(argpack.getSerial())).toString());
        DatagramSend.send(udpSocket, ipmp, tmpaddr);
    }

    public void sendReleaseMsg(Message argpack, Address tmpaddr) {
        Message ipmp = makeMessage(Protocol.RELEASEFILES, (new Long(argpack.getSerial())).toString());
        DatagramSend.send(udpSocket, ipmp, tmpaddr);
    }
}
